home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Applications / Python 1.3.3 / Python 133 SRC / Demo / pdist / cvslib.py < prev    next >
Text File  |  1995-12-21  |  8KB  |  365 lines

  1. """Utilities for CVS administration."""
  2.  
  3. import string
  4. import os
  5. import time
  6. import md5
  7. import fnmatch
  8.  
  9. if not hasattr(time, 'timezone'):
  10.     time.timezone = 0
  11.  
  12. class File:
  13.  
  14.     """Represent a file's status.
  15.  
  16.     Instance variables:
  17.  
  18.     file -- the filename (no slashes), None if uninitialized
  19.     lseen -- true if the data for the local file is up to date
  20.     eseen -- true if the data from the CVS/Entries entry is up to date
  21.              (this implies that the entry must be written back)
  22.     rseen -- true if the data for the remote file is up to date
  23.     proxy -- RCSProxy instance used to contact the server, or None
  24.  
  25.     Note that lseen and rseen don't necessary mean that a local
  26.     or remote file *exists* -- they indicate that we've checked it.
  27.     However, eseen means that this instance corresponds to an
  28.     entry in the CVS/Entries file.
  29.  
  30.     If lseen is true:
  31.     
  32.     lsum -- checksum of the local file, None if no local file
  33.     lctime -- ctime of the local file, None if no local file
  34.     lmtime -- mtime of the local file, None if no local file
  35.  
  36.     If eseen is true:
  37.  
  38.     erev -- revision, None if this is a no revision (not '0')
  39.     enew -- true if this is an uncommitted added file
  40.     edeleted -- true if this is an uncommitted removed file
  41.     ectime -- ctime of last local file corresponding to erev
  42.     emtime -- mtime of last local file corresponding to erev
  43.     extra -- 5th string from CVS/Entries file
  44.  
  45.     If rseen is true:
  46.  
  47.     rrev -- revision of head, None if non-existent
  48.     rsum -- checksum of that revision, Non if non-existent
  49.  
  50.     If eseen and rseen are both true:
  51.     
  52.     esum -- checksum of revision erev, None if no revision
  53.  
  54.     Note
  55.     """
  56.  
  57.     def __init__(self, file = None):
  58.         if file and '/' in file:
  59.             raise ValueError, "no slash allowed in file"
  60.         self.file = file
  61.         self.lseen = self.eseen = self.rseen = 0
  62.         self.proxy = None
  63.  
  64.     def __cmp__(self, other):
  65.         return cmp(self.file, other.file)
  66.  
  67.     def getlocal(self):
  68.         try:
  69.             self.lmtime, self.lctime = os.stat(self.file)[-2:]
  70.         except os.error:
  71.             self.lmtime = self.lctime = self.lsum = None
  72.         else:
  73.             self.lsum = md5.md5(open(self.file).read()).digest()
  74.         self.lseen = 1
  75.  
  76.     def getentry(self, line):
  77.         words = string.splitfields(line, '/')
  78.         if self.file and words[1] != self.file:
  79.             raise ValueError, "file name mismatch"
  80.         self.file = words[1]
  81.         self.erev = words[2]
  82.         self.edeleted = 0
  83.         self.enew = 0
  84.         self.ectime = self.emtime = None
  85.         if self.erev[:1] == '-':
  86.             self.edeleted = 1
  87.             self.erev = self.erev[1:]
  88.         if self.erev == '0':
  89.             self.erev = None
  90.             self.enew = 1
  91.         else:
  92.             dates = words[3]
  93.             self.ectime = unctime(dates[:24])
  94.             self.emtime = unctime(dates[25:])
  95.         self.extra = words[4]
  96.         if self.rseen:
  97.             self.getesum()
  98.         self.eseen = 1
  99.  
  100.     def getremote(self, proxy = None):
  101.         if proxy:
  102.             self.proxy = proxy
  103.         try:
  104.             self.rrev = self.proxy.head(self.file)
  105.         except (os.error, IOError):
  106.             self.rrev = None
  107.         if self.rrev:
  108.             self.rsum = self.proxy.sum(self.file)
  109.         else:
  110.             self.rsum = None
  111.         if self.eseen:
  112.             self.getesum()
  113.         self.rseen = 1
  114.  
  115.     def getesum(self):
  116.         if self.erev == self.rrev:
  117.             self.esum = self.rsum
  118.         elif self.erev:
  119.             name = (self.file, self.erev)
  120.             self.esum = self.proxy.sum(name)
  121.         else:
  122.             self.esum = None
  123.  
  124.     def putentry(self):
  125.         """Return a line suitable for inclusion in CVS/Entries.
  126.  
  127.         The returned line is terminated by a newline.
  128.         If no entry should be written for this file,
  129.         return "".
  130.         """
  131.         if not self.eseen:
  132.             return ""
  133.  
  134.         rev = self.erev or '0'
  135.         if self.edeleted:
  136.             rev = '-' + rev
  137.         if self.enew:
  138.             dates = 'Initial ' + self.file
  139.         else:
  140.             dates = gmctime(self.ectime) + ' ' + \
  141.                 gmctime(self.emtime)
  142.         return "/%s/%s/%s/%s/\n" % (
  143.             self.file,
  144.             rev,
  145.             dates,
  146.             self.extra)
  147.  
  148.     def report(self):
  149.         print '-'*50
  150.         def r(key, repr=repr, self=self):
  151.             try:
  152.                 value = repr(getattr(self, key))
  153.             except AttributeError:
  154.                 value = "?"
  155.             print "%-15s:" % key, value
  156.         r("file")
  157.         if self.lseen:
  158.             r("lsum", hexify)
  159.             r("lctime", gmctime)
  160.             r("lmtime", gmctime)
  161.         if self.eseen:
  162.             r("erev")
  163.             r("enew")
  164.             r("edeleted")
  165.             r("ectime", gmctime)
  166.             r("emtime", gmctime)
  167.         if self.rseen:
  168.             r("rrev")
  169.             r("rsum", hexify)
  170.             if self.eseen:
  171.                 r("esum", hexify)
  172.  
  173.  
  174. class CVS:
  175.     
  176.     """Represent the contents of a CVS admin file (and more).
  177.  
  178.     Class variables:
  179.  
  180.     FileClass -- the class to be instantiated for entries
  181.                  (this should be derived from class File above)
  182.     IgnoreList -- shell patterns for local files to be ignored
  183.  
  184.     Instance variables:
  185.  
  186.     entries -- a dictionary containing File instances keyed by
  187.                their file name
  188.     proxy -- an RCSProxy instance, or None
  189.     """
  190.     
  191.     FileClass = File
  192.  
  193.     IgnoreList = ['.*', '@*', ',*', '*~', '*.o', '*.a', '*.so', '*.pyc']
  194.     
  195.     def __init__(self):
  196.         self.entries = {}
  197.         self.proxy = None
  198.     
  199.     def setproxy(self, proxy):
  200.         if proxy is self.proxy:
  201.             return
  202.         self.proxy = proxy
  203.         for e in self.entries.values():
  204.             e.rseen = 0
  205.     
  206.     def getentries(self):
  207.         """Read the contents of CVS/Entries"""
  208.         self.entries = {}
  209.         f = self.cvsopen("Entries")
  210.         while 1:
  211.             line = f.readline()
  212.             if not line: break
  213.             e = self.FileClass()
  214.             e.getentry(line)
  215.             self.entries[e.file] = e
  216.         f.close()
  217.     
  218.     def putentries(self):
  219.         """Write CVS/Entries back"""
  220.         f = self.cvsopen("Entries", 'w')
  221.         for e in self.values():
  222.             f.write(e.putentry())
  223.         f.close()
  224.  
  225.     def getlocalfiles(self):
  226.         list = self.entries.keys()
  227.         addlist = os.listdir(os.curdir)
  228.         for name in addlist:
  229.             if name in list:
  230.                 continue
  231.             if not self.ignored(name):
  232.                 list.append(name)
  233.         list.sort()
  234.         for file in list:
  235.             try:
  236.                 e = self.entries[file]
  237.             except KeyError:
  238.                 e = self.entries[file] = self.FileClass(file)
  239.             e.getlocal()
  240.  
  241.     def getremotefiles(self, proxy = None):
  242.         if proxy:
  243.             self.proxy = proxy
  244.         if not self.proxy:
  245.             raise RuntimeError, "no RCS proxy"
  246.         addlist = self.proxy.listfiles()
  247.         for file in addlist:
  248.             try:
  249.                 e = self.entries[file]
  250.             except KeyError:
  251.                 e = self.entries[file] = self.FileClass(file)
  252.             e.getremote(self.proxy)
  253.  
  254.     def report(self):
  255.         for e in self.values():
  256.             e.report()
  257.         print '-'*50
  258.     
  259.     def keys(self):
  260.         keys = self.entries.keys()
  261.         keys.sort()
  262.         return keys
  263.  
  264.     def values(self):
  265.         def value(key, self=self):
  266.             return self.entries[key]
  267.         return map(value, self.keys())
  268.  
  269.     def items(self):
  270.         def item(key, self=self):
  271.             return (key, self.entries[key])
  272.         return map(item, self.keys())
  273.  
  274.     def cvsexists(self, file):
  275.         file = os.path.join("CVS", file)
  276.         return os.path.exists(file)
  277.     
  278.     def cvsopen(self, file, mode = 'r'):
  279.         file = os.path.join("CVS", file)
  280.         if 'r' not in mode:
  281.             self.backup(file)
  282.         return open(file, mode)
  283.     
  284.     def backup(self, file):
  285.         if os.path.isfile(file):
  286.             bfile = file + '~'
  287.             try: os.unlink(bfile)
  288.             except os.error: pass
  289.             os.rename(file, bfile)
  290.  
  291.     def ignored(self, file):
  292.         if os.path.isdir(file): return 1
  293.         for pat in self.IgnoreList:
  294.             if fnmatch.fnmatch(file, pat): return 1
  295.         return 0
  296.  
  297.  
  298. # hexify and unhexify are useful to print MD5 checksums in hex format
  299.  
  300. hexify_format = '%02x' * 16
  301. def hexify(sum):
  302.     "Return a hex representation of a 16-byte string (e.g. an MD5 digest)"
  303.     if sum is None:
  304.         return "None"
  305.     return hexify_format % tuple(map(ord, sum))
  306.  
  307. def unhexify(hexsum):
  308.     "Return the original from a hexified string"
  309.     if hexsum == "None":
  310.         return None
  311.     sum = ''
  312.     for i in range(0, len(hexsum), 2):
  313.         sum = sum + chr(string.atoi(hexsum[i:i+2], 16))
  314.     return sum
  315.  
  316.  
  317. unctime_monthmap = {}
  318. def unctime(date):
  319.     if date == "None": return None
  320.     if not unctime_monthmap:
  321.         months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
  322.               'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
  323.         i = 0
  324.         for m in months:
  325.             i = i+1
  326.             unctime_monthmap[m] = i
  327.     words = string.split(date) # Day Mon DD HH:MM:SS YEAR
  328.     year = string.atoi(words[4])
  329.     month = unctime_monthmap[words[1]]
  330.     day = string.atoi(words[2])
  331.     [hh, mm, ss] = map(string.atoi, string.splitfields(words[3], ':'))
  332.     ss = ss - time.timezone
  333.     return time.mktime((year, month, day, hh, mm, ss, 0, 0, 0))
  334.  
  335. def gmctime(t):
  336.     if t is None: return "None"
  337.     return time.asctime(time.gmtime(t))
  338.  
  339. def test_unctime():
  340.     now = int(time.time())
  341.     t = time.gmtime(now)
  342.     at = time.asctime(t)
  343.     print 'GMT', now, at
  344.     print 'timezone', time.timezone
  345.     print 'local', time.ctime(now)
  346.     u = unctime(at)
  347.     print 'unctime()', u
  348.     gu = time.gmtime(u)
  349.     print '->', gu
  350.     print time.asctime(gu)
  351.  
  352. def test():
  353.     x = CVS()
  354.     x.getentries()
  355.     x.getlocalfiles()
  356. ##    x.report()
  357.     import rcsclient
  358.     proxy = rcsclient.openrcsclient()
  359.     x.getremotefiles(proxy)
  360.     x.report()
  361.  
  362.  
  363. if __name__ == "__main__":
  364.     test()
  365.